package com.baoyouqun.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.cola.dto.Response;
import com.alibaba.cola.dto.SingleResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baoyouqun.base.SystemConstant;
import com.baoyouqun.base.utils.ConvertUtils;
import com.baoyouqun.base.utils.ExcelTool;
import com.baoyouqun.domain.CreateCmd.UserPointsRecordCreateCmd;
import com.baoyouqun.domain.CreateCmd.UserPointsWriteOffCreateCmd;
import com.baoyouqun.domain.Query.UserPointsRecordQuery;
import com.baoyouqun.domain.UpdateCmd.UserPointsRecordUpdateCmd;
import com.baoyouqun.domain.UpdateCmd.UserPointsWriteOffUpdateCmd;
import com.baoyouqun.domain.VO.UserPointsRecordVO;
import com.baoyouqun.domain.VO.UserPointsVO;
import com.baoyouqun.entity.User;
import com.baoyouqun.entity.UserPoints;
import com.baoyouqun.entity.UserPointsRecord;
import com.baoyouqun.mapper.UserPointsRecordMapper;
import com.baoyouqun.service.UserPointsRecordService;
import com.baoyouqun.service.UserPointsService;
import com.baoyouqun.service.UserService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.util.StringUtil;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Service
public class UserPointsRecordServiceImpl extends ServiceImpl<UserPointsRecordMapper, UserPointsRecord> implements UserPointsRecordService {

    @Resource
    private UserPointsRecordMapper userPointsRecordMapper;

    @Resource
    private UserPointsService userPointsService; // 用于操作用户积分

    @Resource
    private UserService userService; // 用于查询用户名

    // ---------------------- 原有方法保留（不变） ----------------------
    @Override
    public SingleResponse<UserPointsRecordVO> create(UserPointsRecordCreateCmd cmd) {
        UserPointsRecord entity = ConvertUtils.copyBean(cmd, UserPointsRecord.class);
        Boolean result = save(entity);
        if (!result) {
            return SingleResponse.buildFailure("100101", "创建失败");
        }
        UserPointsRecordVO vo = ConvertUtils.copyBean(entity, UserPointsRecordVO.class);
        return SingleResponse.of(vo);
    }

    @Override
    public Response update(UserPointsRecordUpdateCmd cmd) {
        if (Objects.isNull(cmd)) {
            return Response.buildFailure("100301", "参数不能为空");
        }
        if (StringUtil.isBlank(cmd.getId())) {
            return Response.buildFailure("100302", "ID不能为空");
        }
        UserPointsRecord oldEntity = getById(cmd.getId());
        if (Objects.isNull(oldEntity)) {
            return Response.buildFailure("100303", "未查询到此数据");
        }
        oldEntity = ConvertUtils.copyBean(cmd, UserPointsRecord.class);
        Boolean result = updateById(oldEntity);
        return result ? Response.buildSuccess() : Response.buildFailure("100304", "更新失败");
    }

    @Override
    public UserPointsRecordVO selectById(String id) {
        UserPointsRecord entity = userPointsRecordMapper.selectById(id);
        if (Objects.isNull(entity)) {
            return null;
        }
        UserPointsRecordVO vo = ConvertUtils.copyBean(entity, UserPointsRecordVO.class);
        // 补充用户名和类型描述
        fillVoExtInfo(vo);
        return vo;
    }

    @Override
    public IPage<UserPointsRecordVO> pageVo(UserPointsRecordQuery query) {
        IPage<UserPointsRecord> page = new Page<>(query.getCurrent(), query.getSize());
        page = page(page, new LambdaQueryWrapper<UserPointsRecord>());
        Page<UserPointsRecordVO> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        List<UserPointsRecordVO> records = ConvertUtils.copyBeanList(page.getRecords(), UserPointsRecordVO.class);
        fillVoListExtInfo(records); // 补充扩展信息
        voPage.setRecords(records);
        return voPage;
    }

    // ---------------------- 新增核销相关方法实现 ----------------------

    /**
     * 1. 分页查询核销记录（仅查changeType=3，关联用户名）
     */
    @Override
    public IPage<UserPointsRecordVO> pageWriteOffVo(UserPointsRecordQuery query) {
        // 1. 参数校验
        if (Objects.isNull(query)) {
            query = new UserPointsRecordQuery();
        }
        // 强制筛选“积分兑换扣减”（changeType=3）
        query.setChangeType((byte) 3);

        // 2. 构建分页对象
        Page<UserPointsRecord> page = new Page<>(query.getCurrent(), query.getSize());

        // 3. 构建筛选条件（支持用户名、时间范围、操作人）
        LambdaQueryWrapper<UserPointsRecord> wrapper = buildWriteOffQueryWrapper(query);

        // 4. MP分页查询
        page = this.page(page, wrapper);

        // 5. 转换VO+补充扩展信息（用户名、类型描述）
        List<UserPointsRecordVO> voList = ConvertUtils.copyBeanList(page.getRecords(), UserPointsRecordVO.class);
        fillVoListExtInfo(voList);

        // 6. 封装分页结果
        Page<UserPointsRecordVO> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        voPage.setRecords(voList);
        return voPage;
    }

    /**
     * 2. 创建积分核销记录（扣减用户积分，事务保证）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public SingleResponse<UserPointsRecordVO> createWriteOff(UserPointsWriteOffCreateCmd cmd) {
        // 1. 参数校验
        if (!cmd.isValidBizDesc()) {
            return SingleResponse.buildFailure(cmd.getBizDescErrorMsg().split(":", 2)[0],
                    cmd.getBizDescErrorMsg().split(":", 2)[1]);
        }

        // 2. 查询用户积分信息
        UserPointsVO userPointsVO = userPointsService.getByUserId(cmd.getUserId());
        if (Objects.isNull(userPointsVO)) {
            return SingleResponse.buildFailure("100401", "未查询到用户积分账户");
        }

        // 3. 校验积分是否足够（核销积分数 <= 当前可用积分）
        int writeOffPoints = -cmd.getWriteOffPoints(); // 转为负数（扣减）
        if (userPointsVO.getCurrentPoints() + writeOffPoints < 0) { // currentPoints - 核销数 < 0
            return SingleResponse.buildFailure("100402", "用户积分不足，当前可用：" + userPointsVO.getCurrentPoints());
        }

        // 4. 计算积分变动前后值
        int pointsBefore = userPointsVO.getCurrentPoints();
        int pointsAfter = pointsBefore + writeOffPoints;

        // 5. 创建核销记录（changeType=3）
        UserPointsRecord writeOffRecord = new UserPointsRecord();
        writeOffRecord.setUserId(cmd.getUserId());
        writeOffRecord.setChangePoints(writeOffPoints);
        writeOffRecord.setChangeType((byte) 3); // 积分兑换扣减
        writeOffRecord.setBizDesc(cmd.getBizDesc());
        writeOffRecord.setPointsBeforeChange(pointsBefore);
        writeOffRecord.setPointsAfterChange(pointsAfter);
        writeOffRecord.setCreateTime(LocalDateTime.now());
        writeOffRecord.setOperator(cmd.getOperator());
        writeOffRecord.setType(cmd.getType());
        boolean saveSuccess = this.save(writeOffRecord);
        if (!saveSuccess) {
            throw new RuntimeException("核销记录创建失败");
        }

        // 6. 更新用户积分（扣减可用积分）
        UserPoints userPoints = ConvertUtils.copyBean(userPointsVO, UserPoints.class);
        userPoints.setCurrentPoints(pointsAfter);
        userPoints.setUpdateTime(LocalDateTime.now());
        boolean updatePointsSuccess = userPointsService.updateById(userPoints);
        if (!updatePointsSuccess) {
            throw new RuntimeException("用户积分扣减失败");
        }

        // 7. 转换VO并补充扩展信息
        UserPointsRecordVO vo = ConvertUtils.copyBean(writeOffRecord, UserPointsRecordVO.class);
        fillVoExtInfo(vo);
        return SingleResponse.of(vo);
    }

    /**
     * 3. 编辑积分核销记录（调整积分扣减，事务保证）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response updateWriteOff(UserPointsWriteOffUpdateCmd cmd) {
        // 1. 参数校验
        if (!cmd.isValidNewBizDesc()) {
            return Response.buildFailure(cmd.getNewBizDescErrorMsg().split(":", 2)[0],
                    cmd.getNewBizDescErrorMsg().split(":", 2)[1]);
        }

        // 2. 查询旧核销记录（必须是changeType=3）
        UserPointsRecord oldRecord = this.getById(cmd.getId());
        if (Objects.isNull(oldRecord)) {
            return Response.buildFailure("100303", "未查询到核销记录");
        }
        if (!oldRecord.getChangeType().equals((byte) 3)) {
            return Response.buildFailure("100403", "该记录不是核销记录，无法编辑");
        }

        // 3. 计算积分差额（新扣减数 - 旧扣减数）
        int oldWriteOffPoints = -oldRecord.getChangePoints(); // 旧扣减数（转为正数）
        int newWriteOffPoints = cmd.getNewWriteOffPoints();
        int pointsDiff = oldWriteOffPoints - newWriteOffPoints; // 差额：正=需补回积分，负=需多扣积分

        // 4. 查询用户当前积分
        UserPointsVO userPointsVO = userPointsService.getByUserId(oldRecord.getUserId());
        if (Objects.isNull(userPointsVO)) {
            return Response.buildFailure("100401", "未查询到用户积分账户");
        }

        // 5. 校验调整后积分是否合法（若需多扣，确保积分足够）
        int newCurrentPoints = userPointsVO.getCurrentPoints() + pointsDiff;
        if (newCurrentPoints < 0) {
            return Response.buildFailure("100402", "用户积分不足，调整后可用：" + newCurrentPoints);
        }

        // 6. 更新核销记录
        oldRecord.setChangePoints(-newWriteOffPoints); // 新扣减数（转为负数）
        oldRecord.setBizDesc(cmd.getNewBizDesc());
        oldRecord.setType(cmd.getType());
        oldRecord.setPointsBeforeChange(newCurrentPoints - (-newWriteOffPoints)); // 新变动前积分
        oldRecord.setPointsAfterChange(newCurrentPoints); // 新变动后积分
        boolean updateRecordSuccess = this.updateById(oldRecord);
        if (!updateRecordSuccess) {
            throw new RuntimeException("核销记录更新失败");
        }

        // 7. 更新用户积分
        UserPoints userPoints = ConvertUtils.copyBean(userPointsVO, UserPoints.class);
        userPoints.setCurrentPoints(newCurrentPoints);
        userPoints.setUpdateTime(LocalDateTime.now());
        boolean updatePointsSuccess = userPointsService.updateById(userPoints);
        if (!updatePointsSuccess) {
            throw new RuntimeException("用户积分调整失败");
        }

        return Response.buildSuccess();
    }

    /**
     * 4. 删除积分核销记录（恢复用户积分，事务保证）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response deleteWriteOff(String id) {
        // 1. 参数校验
        if (StringUtil.isBlank(id)) {
            return Response.buildFailure("100302", "核销记录ID不能为空");
        }

        // 2. 查询核销记录（必须是changeType=3）
        UserPointsRecord writeOffRecord = this.getById(id);
        if (Objects.isNull(writeOffRecord)) {
            return Response.buildFailure("100303", "未查询到核销记录");
        }
        if (!writeOffRecord.getChangeType().equals((byte) 3)) {
            return Response.buildFailure("100403", "该记录不是核销记录，无法删除");
        }

        // 3. 查询用户当前积分
        UserPointsVO userPointsVO = userPointsService.getByUserId(writeOffRecord.getUserId());
        if (Objects.isNull(userPointsVO)) {
            return Response.buildFailure("100401", "未查询到用户积分账户");
        }

        // 4. 恢复用户积分（将扣减的积分加回）
        int recoverPoints = -writeOffRecord.getChangePoints(); // 原扣减数为负，恢复为正
        int newCurrentPoints = userPointsVO.getCurrentPoints() + recoverPoints;
        UserPoints userPoints = ConvertUtils.copyBean(userPointsVO, UserPoints.class);
        userPoints.setCurrentPoints(newCurrentPoints);
        userPoints.setUpdateTime(LocalDateTime.now());
        boolean updatePointsSuccess = userPointsService.updateById(userPoints);
        if (!updatePointsSuccess) {
            throw new RuntimeException("用户积分恢复失败");
        }

        // 5. 删除核销记录
        boolean deleteSuccess = this.removeById(id);
        if (!deleteSuccess) {
            throw new RuntimeException("核销记录删除失败");
        }

        return Response.buildSuccess();
    }

    /**
     * 5. 导出核销记录Excel（按筛选条件全量导出）
     */
    @Override
    public void exportWriteOffExcel(UserPointsRecordQuery query, HttpServletResponse response) {
        // 1. 参数校验
        if (Objects.isNull(query)) {
            query = new UserPointsRecordQuery();
        }
        // 强制筛选“积分兑换扣减”（changeType=3）
        query.setChangeType((byte) 3);
        // 全量查询（超大size）
        query.setCurrent(1L);
        query.setSize(Long.MAX_VALUE);

        try {
            // 2. 查询全量核销记录
            LambdaQueryWrapper<UserPointsRecord> wrapper = buildWriteOffQueryWrapper(query);
            List<UserPointsRecord> recordList = this.list(wrapper);
            if (CollUtil.isEmpty(recordList)) {
                throw new RuntimeException("无符合条件的核销记录，无法导出");
            }

            // 3. 转换VO+补充扩展信息
            List<UserPointsRecordVO> voList = ConvertUtils.copyBeanList(recordList, UserPointsRecordVO.class);
            fillVoListExtInfo(voList);

            // 4. 创建Excel工作簿（XSSF格式）
            Workbook workbook = new XSSFWorkbook();
            Sheet sheet = workbook.createSheet("积分核销记录");
            sheet.setDefaultColumnWidth(25); // 默认列宽

            // 5. 构建表头（与页面列一一对应）
            String[] headers = {
                    "核销对象", "核销类型", "核销描述", "核销积分数", "操作人", "核销时间", "关联单号", "备注"
            };
            // 表头样式（加粗）
            CellStyle headerStyle = workbook.createCellStyle();
            Font headerFont = workbook.createFont();
            headerFont.setBold(true);
            headerStyle.setFont(headerFont);
            // 写入表头
            Row headerRow = sheet.createRow(0);
            for (int i = 0; i < headers.length; i++) {
                Cell cell = headerRow.createCell(i);
                cell.setCellValue(headers[i]);
                cell.setCellStyle(headerStyle);
            }

            // 6. 填充数据行
            DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            for (int i = 0; i < voList.size(); i++) {
                UserPointsRecordVO vo = voList.get(i);
                Row dataRow = sheet.createRow(i + 1);

                // 核销对象（用户名）
                dataRow.createCell(0).setCellValue(StrUtil.blankToDefault(vo.getUserName(), "未知用户"));
                // 核销类型（中文描述）
                dataRow.createCell(1).setCellValue(StrUtil.blankToDefault(vo.getChangeTypeDesc(), "积分兑换扣减"));
                // 核销描述
                dataRow.createCell(2).setCellValue(StrUtil.blankToDefault(vo.getBizDesc(), ""));
                // 核销积分数（转为正数展示，如-1000→1000）
                int showPoints = -vo.getChangePoints();
                dataRow.createCell(3).setCellValue(showPoints);
                // 操作人
                dataRow.createCell(4).setCellValue(StrUtil.blankToDefault(vo.getOperator(), "未知操作人"));
                // 核销时间
                String createTime = Objects.nonNull(vo.getCreateTime()) ? vo.getCreateTime().format(dtf) : "";
                dataRow.createCell(5).setCellValue(createTime);
                // 关联单号
                dataRow.createCell(6).setCellValue(StrUtil.blankToDefault(vo.getBizNo(), "无"));
                // 备注
                dataRow.createCell(7).setCellValue(StrUtil.blankToDefault(vo.getRemark(), "无"));
            }

            // 7. 设置响应头（解决中文乱码）
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            String fileName = "积分核销记录_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + ".xlsx";
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Pragma", "public");
            response.setHeader("Cache-Control", "max-age=30");
            ExcelTool.setExportFileName(response, "积分核销记录");

            // 8. 输出Excel流
            try (OutputStream outputStream = response.getOutputStream()) {
                workbook.write(outputStream);
                outputStream.flush();
            } finally {
                workbook.close(); // 释放资源
            }

        } catch (IOException e) {
            log.error("导出核销记录Excel：IO异常", e);
            throw new RuntimeException("导出Excel失败：" + e.getMessage());
        } catch (Exception e) {
            log.error("导出核销记录Excel：未知异常", e);
            throw new RuntimeException("导出Excel异常：" + e.getMessage());
        }
    }

    // ---------------------- 工具方法（复用逻辑） ----------------------

    /**
     * 构建核销记录查询条件（支持用户名、时间范围、操作人）
     */
    private LambdaQueryWrapper<UserPointsRecord> buildWriteOffQueryWrapper(UserPointsRecordQuery query) {
        LambdaQueryWrapper<UserPointsRecord> wrapper = new LambdaQueryWrapper<>();
        // 1. 核销类型（强制3）
        wrapper.eq(UserPointsRecord::getChangeType, query.getChangeType());
        // 5. 排序：按核销时间倒序
        wrapper.orderByDesc(UserPointsRecord::getCreateTime);

        return wrapper;
    }

    /**
     * 批量补充VO扩展信息（用户名、类型中文描述）
     */
    private void fillVoListExtInfo(List<UserPointsRecordVO> voList) {
        if (CollUtil.isEmpty(voList)) {
            return;
        }

        // 1. 批量查询用户名（一次查库）
        List<String> userIds = voList.stream()
                .map(UserPointsRecordVO::getUserId)
                .filter(StrUtil::isNotEmpty)
                .distinct()
                .collect(Collectors.toList());
        Map<String, String> userIdToNameMap = new HashMap<>();
        if (CollUtil.isNotEmpty(userIds)) {
            List<User> userList = userService.listByIds(userIds);
            userIdToNameMap = userList.stream()
                    .collect(Collectors.toMap(User::getId, User::getName, (k1, k2) -> k1));
        }

        // 2. 填充信息
        for (UserPointsRecordVO vo : voList) {
            // 用户名
            vo.setUserName(userIdToNameMap.getOrDefault(vo.getUserId(), SystemConstant.DEFAULT_NAME));
            // 核销类型中文描述
            vo.setChangeTypeDesc(getChangeTypeDesc(vo.getChangeType()));
        }
    }

    /**
     * 单个补充VO扩展信息
     */
    private void fillVoExtInfo(UserPointsRecordVO vo) {
        if (Objects.isNull(vo)) {
            return;
        }

        // 用户名
        User user = userService.getById(vo.getUserId());
        vo.setUserName(Objects.nonNull(user) ? user.getName() : SystemConstant.DEFAULT_NAME);
        // 核销类型中文描述
        vo.setChangeTypeDesc(getChangeTypeDesc(vo.getChangeType()));
    }

    /**
     * 转换变动类型为中文描述
     */
    private String getChangeTypeDesc(Byte changeType) {
        return switch (Objects.requireNonNullElse(changeType, (byte) 0)) {
            case 1 -> "充值赠送";
            case 2 -> "任务奖励";
            case 3 -> "积分兑换扣减"; // 核销类型
            case 4 -> "系统调整";
            case 5 -> "其他";
            default -> "未知类型";
        };
    }
}